// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{fmt::Display, str::FromStr};

use clap::ValueEnum;
use lazy_static::lazy_static;
use nom::{branch::alt, bytes::complete::tag, combinator::{all_consuming, map}, IResult};
use serde::{Deserialize, Serialize};
use strum_macros::EnumCount;

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumCount, ValueEnum)]
#[serde(rename_all = "snake_case")]
#[clap(rename_all = "snake_case")]
pub enum Arch {
    X86_64,
    Arm,
    #[serde(rename = "aarch64")]
    #[value(name = "aarch64")]
    AArch64,
}

impl Display for Arch {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.as_str().fmt(f)
    }
}

lazy_static! {
    static ref HOST_ARCH: Arch = std::env::var("FAKE_HOST_ARCH")
        .map(|s| s.parse().unwrap_or(Arch::DEFAULT_HOST))
        .unwrap_or(Arch::DEFAULT_HOST);
}

impl Arch {
    #[cfg(target_arch = "x86_64")]
    const DEFAULT_HOST: Self = Self::X86_64;
    #[cfg(target_arch = "arm")]
    const DEFAULT_HOST: Self = Self::Arm;
    #[cfg(target_arch = "aarch64")]
    const DEFAULT_HOST: Self = Self::AArch64;

    pub fn parse_str(i: &str) -> IResult<&str, Self> {
        alt((
            map(tag("x86_64"), |_| Self::X86_64),
            map(tag("arm"), |_| Self::Arm),
            map(tag("aarch64"), |_| Self::AArch64),
        ))(i)
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            Arch::X86_64 => "x86_64",
            Arch::Arm => "arm",
            Arch::AArch64 => "aarch64",
        }
    }

    #[inline]
    pub fn host() -> Self {
        *HOST_ARCH
    }

    pub fn is_host(&self) -> bool {
        *self == Self::host()
    }
}

impl Default for Arch {
    fn default() -> Self {
        Self::host()
    }
}

impl FromStr for Arch {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (_, arch) = all_consuming(Self::parse_str)(s).map_err(|_| ())?;
        Ok(arch)
    }
}

impl TryFrom<u8> for Arch {
    type Error = u8;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::X86_64),
            1 => Ok(Self::Arm),
            2 => Ok(Self::AArch64),
            _ => Err(value),
        }
    }
}